// --------------------------------------------------------------------------------------------------------------------- 
// <copyright file="WeightedMovingAverage.cs" company="Scrum for Team System">
//   None
// </copyright>
// <summary>
//   Weighted Moving Average model: like the simple Moving Average except that it applies a weighting when
//   calculating the average to give more weight to the most recent values.
// </summary>
// ---------------------------------------------------------------------------------------------------------------------

namespace ScrumForTeamSystem.Metrics
{
    /// <summary>
    /// Weighted Moving Average model: like the simple Moving Average except that it applies a weighting when
    /// calculating the average to give more weight to the most recent values.
    /// </summary>
    internal class WeightedMovingAverage : MovingAverage
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="WeightedMovingAverage"/> class. 
        /// Constructor: sets the period
        /// </summary>
        /// <param name="period">
        /// the number of days in the average
        /// </param>
        public WeightedMovingAverage(int period) : base(period)
        {
        }

        /// <summary>
        /// Calculate a weighted average, e.g. for a 5-day period use
        /// 1/15 of day 1's value,
        /// 2/15 of day 2's value,
        /// 3/15 of day 3's value,
        /// 4/15 of day 4's value,
        /// 5/15 of day 5's value,
        /// where day 1 is the first day of the period and day 5 is the last
        /// </summary>
        /// <param name="index">
        /// The index.
        /// </param>
        /// <returns>
        /// a weighted average
        /// </returns>
        protected override double? CalculateAverage(int index)
        {
            double divisor = this.Divisor(this.Period);
            double total = 0;
            for (int i = 0; i < this.Period; i++)
            {
                total += (double)this.Data[index - i] * (this.Period - i) / divisor;
            }

            return total;
        }

        /// <summary>
        /// Recursively calculate the divisor for the weights, e.g 1+2+3 for a 3-day period, 1+2+3+4+5 for a 5-day period
        /// </summary>
        /// <param name="period">
        /// period, e.g. 5 for a 5-day average
        /// </param>
        /// <returns>
        /// the weight divisor
        /// </returns>
        private int Divisor(int period)
        {
            return period == 1 ? 1 : period + this.Divisor(period - 1);
        }
    }
}